package com.basker.pisces.domain.command.run;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

import org.springframework.util.CollectionUtils;

import com.basker.pisces.domain.command.CommandExecuteContext;
import com.basker.pisces.domain.command.CommandResult;
import com.basker.pisces.domain.command.exception.CommandExecuteRuntimeException;
import com.basker.pisces.domain.command.meta.Command;
import com.basker.pisces.domain.command.processor.ICommandDataPrepared;
import com.basker.pisces.domain.command.processor.ICommandExecuteProcessor;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.meta.TextProperty;
import com.basker.pisces.domain.validation.IDependOnValidation;
import com.basker.pisces.domain.validation.ValidationErrors;
import com.basker.pisces.domain.validation.validator.ICommandValidator;
import com.basker.pisces.domain.validation.validator.LockValidator;
import com.basker.pisces.spring.SpringApplicationContext;

/**
 * 命令运行器，每个{@link Command}实例在执行时都会生成一个当前类的子类，确保这些子类添加了Command注解
 *
 * @param <TCommand>
 * @author hangwen
 */
public abstract class CommandRunner<TCommand extends Command> {
    private AtomicBoolean initialed = new AtomicBoolean(false);

    private CommandExecuteContext context;
    private List<ICommandValidator> validators;

    protected CommandExecuteEventPublisher eventPublisher = new CommandExecuteEventPublisher(this);

    private LockValidator lockValidator;

    @SuppressWarnings("unchecked")
    public TCommand getCommand() {
        return (TCommand) this.getContext().getCommand();
    }

    public CommandExecuteContext getContext() {
        return context;
    }

    /**
     * 获取执行操作需要加载的对象字段,由以下部分组成
     *
     * <li>1.{@link #getRequiredPropertyAliasList()}
     * <li>2.{@link ICommandDataPrepared#getRequiredPropertyAliasList()},其中，验证器{@link ICommandValidator}和处理器
     * {@link ICommandExecuteProcessor}都继承了该接口
     * <li>3.如果前两项不为空，那么再加上实体的代码和名称字段
     *
     * @return
     * @see {@link ICommandValidator#getRequiredPropertyAliasList()}
     */
    public final List<String> getRequiedProprertyAlias() {
        List<String> properties = new ArrayList<>();

        List<String> propertyNameList = this.getRequiredPropertyAliasList();
        if (!CollectionUtils.isEmpty(propertyNameList)) {
            properties.addAll(propertyNameList);
        }

        List<ICommandDataPrepared> dataPrepareds = this.tryGetCommandDataPrepared();
        if (!CollectionUtils.isEmpty(dataPrepareds)) {
            for (ICommandDataPrepared dataPrepared : dataPrepareds) {
                List<String> fields = dataPrepared.getRequiredPropertyAliasList();

                if (!CollectionUtils.isEmpty(fields)) {
                    properties.addAll(fields);
                }
            }
        }

        if (!CollectionUtils.isEmpty(properties)) {
            TextProperty codeProperty = this.getEntity().getCodeProperty();
            if (codeProperty != null) {
                properties.add(codeProperty.getAlias());
            }

            TextProperty nameProperty = this.getEntity().getNameProperty();
            if (nameProperty != null) {
                properties.add(nameProperty.getAlias());
            }
        }

        return properties;
    }

    /**
     * 初始化
     */
    public void initial() {
        if (!initialed.compareAndSet(false, true)) {
            return;
        }

        // 验证器
        initialValidators();
        // 处理器
        initialProcessors();
    }

    public ValidationErrors lock(Object[] dataObjects) {
        if (this.lockValidator == null) {
            return ValidationErrors.NO_ERROR;
        }

        return this.lockValidator.validate(dataObjects);
    }

    public void unlock() {
        if (this.lockValidator != null) {
            this.lockValidator.doUnLocks();
        }
    }

    protected void addProcessor(ICommandExecuteProcessor processor) {
        this.eventPublisher.addProcessor(processor);
    }

    /**
     * 获取当前命令的实体
     *
     * @return
     */
    protected final Entity getEntity() {
        return this.context.getEntity();
    }

    /**
     * 获取命令运行器内置的事件处理器，子类可重写
     *
     * @return
     */
    protected List<ICommandExecuteProcessor> getInnerProcessors() {
        return Collections.emptyList();
    }

    /**
     * 获取命令运行器内置的校验器，子类可重写
     *
     * @return
     */
    protected List<ICommandValidator> getInnerValidators() {
        return Collections.emptyList();
    }

    /**
     * 获取命令运行需要的属性，默认会调用{@link Command#getRequiredPropertyAliasList()},子类可重写，但不要忘了调用super
     *
     * @return
     */
    protected List<String> getRequiredPropertyAliasList() {
        return new ArrayList<>(this.getCommand().getRequiredPropertyAliasList());
    }

    /**
     * 执行最后调用，无论正常完成还是出错，都会执行,子类可重写
     *
     * @param dataObjects
     */
    protected void onAfterInvoke(Object[] dataObjects) {

    }

    /**
     * 执行中出现异常都会调用该方法,子类可重写
     *
     * @param dataObjects
     */
    protected void onAfterThrowing(Object[] dataObjects, CommandExecuteRuntimeException exception) {

    }

    /**
     * 执行前调用，子类可重写
     *
     * @param dataObjects
     */
    protected void onBeforeInvoke(Object[] dataObjects) {

    }

    /**
     * 完成执行时调用，此时{@link #invoke(Object[])}方法已经正常完成，子类可重写
     *
     * @param dataObjects
     */
    protected void onCompleteInvoke(Object[] dataObjects) {

    }

    final void afterInvoke(Object[] dataObjects) {
        this.onAfterInvoke(dataObjects);

        this.eventPublisher.applyProcessAfterInvoke(dataObjects);
    }

    final void afterThrowing(Object[] dataObjects, CommandExecuteRuntimeException exception) {
        this.onAfterThrowing(dataObjects, exception);

        this.eventPublisher.applyProcessAfterThrowing(dataObjects, exception);
    }

    final void beforeInvoke(Object[] dataObjects) {
        this.onBeforeInvoke(dataObjects);

        this.eventPublisher.applyProcessBeforeInvoke(dataObjects);
    }

    final void completeInvoke(Object[] dataObjects) {
        this.onCompleteInvoke(dataObjects);

        this.eventPublisher.applyProcessCompleteInvoke(dataObjects);
    }

    /**
     * 执行命令的具体逻辑，子类必须重写
     *
     * @param dataObjects
     * @return
     */
    abstract CommandResult invoke(Object[] dataObjects);

    void setContext(CommandExecuteContext context) {
        this.context = context;
    }

    ValidationErrors validate(Object[] dataObjects) {
        ValidationErrors errors = this.validate0(dataObjects);

        return errors;
    }

    private LockValidator createLockValidator() {
        Command command = this.context.getCommand();
        if (!command.getEnableLock()) {
            return null;
        }

        return SpringApplicationContext.getInstance().getBean(LockValidator.class);
    }

    private void initialProcessors() {
        List<ICommandExecuteProcessor> processors = new ArrayList<>();

        List<ICommandExecuteProcessor> innerProcessors = this.getInnerProcessors();
        if (!CollectionUtils.isEmpty(innerProcessors)) {
            processors.addAll(innerProcessors);
        }

        Command command = this.context.getCommand();
        List<ICommandExecuteProcessor> commandProcessors = command.createProcessors();
        if (!CollectionUtils.isEmpty(commandProcessors)) {
            processors.addAll(commandProcessors);
        }

        for (ICommandExecuteProcessor processor : processors) {
            this.addProcessor(processor);
        }
    }

    private void initialValidators() {
        this.validators = new ArrayList<>();

        this.lockValidator = this.createLockValidator();
        if (lockValidator != null) {
            this.validators.add(lockValidator);
        }

        List<ICommandValidator> innerValidators = this.getInnerValidators();
        if (!CollectionUtils.isEmpty(innerValidators)) {
            this.validators.addAll(innerValidators);
        }

        Command command = this.context.getCommand();
        List<ICommandValidator> validaitonValidators = command.createValidators();
        if (!CollectionUtils.isEmpty(validaitonValidators)) {
            for (ICommandValidator commandValidator : validaitonValidators) {
                if (commandValidator instanceof IDependOnValidation) {
                    if (this.getContext().getOption()
                            .isValidationDisable(((IDependOnValidation) commandValidator).getValidation().getName())) {
                        continue;
                    }
                }

                this.validators.add(commandValidator);
            }
        }

        for (ICommandValidator validator : validators) {
            validator.setContext(context);

            this.tryAddProcessor(validator);
        }
    }

    private void tryAddProcessor(Object component) {
        if (component instanceof ICommandExecuteProcessor) {
            this.addProcessor((ICommandExecuteProcessor) component);
        }
    }

    /**
     * 尝试获取当前命令的所有{@link ICommandDataPrepared}的实例
     *
     * @return
     */
    private List<ICommandDataPrepared> tryGetCommandDataPrepared() {
        final List<ICommandDataPrepared> ret = new ArrayList<>();

        Consumer<List<?>> tryAdd = col -> {
            if (CollectionUtils.isEmpty(col)) {
                return;
            }

            for (Object o : col) {
                if (o instanceof ICommandDataPrepared) {
                    ret.add((ICommandDataPrepared) o);
                }
            }
        };

        tryAdd.accept(this.validators);
        tryAdd.accept(this.eventPublisher.getProcessors());

        return ret;
    }

    private ValidationErrors validate0(Object[] dataObjects) {
        ValidationErrors allErrors = null;

        for (ICommandValidator validator : this.validators) {
            ValidationErrors errors = validator.validate(dataObjects);
            if (errors == null || !errors.hasError()) {
                continue;
            }

            if (allErrors == null) {
                allErrors = new ValidationErrors();
            }

            allErrors.merge(errors);
        }

        return allErrors == null ? ValidationErrors.NO_ERROR : allErrors;
    }
}
